﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using log4net;
using SyncLib;
using SyncLib.Client;
using SyncLib.Interface;
using Microsoft.Synchronization;
using SyncLib.Data;
using System.Windows.Forms;
using System.IO;
using System.ServiceModel;
using System.Data;
using System.Threading;

namespace SyncApplication.Business
{
    public class PerformSynchronize
    {
        #region Field
        static ILog Log = LogManager.GetLogger(typeof(Log4NetImplement));

        /// <summary>
        /// Đối tượng tự tạo và quản lý thuộc tính ProxyInfo
        /// </summary>
        private bool selfCreate1 = false;

        /// <summary>
        /// Đối tượng tự tạo và quản lý thuộc tính RemoteProvider
        /// </summary>
        private bool selfCreate2 = false;
        private WCFConnection _wcfConnection = null;
        private string syncDescription = string.Empty;
        private string message = string.Empty;
        private Guid clientId = Guid.Empty;
        private IUrlProvider _UrlProvider;
        private ProxyInfo<ISyncManager> _ProxyInfo;
        private KSyncProviderProxy _RemoteProvider;
        private AnimationGrid _Animation;
        #endregion Field

        #region Properties

        public RunMode Mode { get; set; }
        public Guid SessionId { get; set; }
        public SyncOrchestrator SyncAgent { get; private set; }
        /// <summary>
        /// Thông báo lỗi nếu có (Kết quả Sync là Skip)
        /// </summary>
        public string Message { get; private set; }
        /// <summary>
        /// Thông tin về Channel và Factory của WCF
        /// </summary>
        public ProxyInfo<ISyncManager> ProxyInfo
        {
            get
            {
                if (_ProxyInfo == null)
                {
                    MonitorEvent.Monitor.RaiseCommunicationEvent(SyncEventType.Info, "", "Tạo kết nối tới máy chủ");
                    {
                        _wcfConnection = new WCFConnection();
                        _wcfConnection.Open();

                        _ProxyInfo = _wcfConnection.ProxyInfo;
                        //WcfClientUtil.CreateProxy<ISyncManager>(UrlProvider.Url, UrlProvider.BindingName);
                        selfCreate1 = true;
                    }
                }
                return _ProxyInfo;
            }
            set
            {
                if (_ProxyInfo != null && !_ProxyInfo.Equals(value))
                {
                    EndSelfWCFClient();
                }
                _ProxyInfo = value;
            }
        }

        /// <summary>
        /// Row hiện tại chứa tên của scope cần thực hiện đồng bộ
        /// </summary>
        public ClientSyncDS.TaskSyncListRow TaskSync { get; set; }

        /// <summary>
        ///Thời điểm bắt đầu thực hiện
        /// </summary>
        public DateTime SyncStartTime { get; private set; }
        /// <summary>
        /// Thời điểm kết thúc
        /// </summary>
        public DateTime SyncEndTime { get; private set; }

        public IKeeperSync KeeperSync { get; private set; }
        public string ResultCode { get; private set; }

        public SyncOperationStatistics AgentStatistics { get; private set; }
        public KSyncStatistics LocalKStatistics { get; private set; }
        public KSyncStatistics RemoteKStatistics { get; private set; }
        public IUrlProvider UrlProvider
        {
            get
            {
                if (_UrlProvider == null)
                    return ClientSyncContext.UrlProvider;
                else
                    return _UrlProvider;
            }
            set { _UrlProvider = value; }
        }
        public PerformSyncStep Step { get; private set; }
        public ScopeInfoEx Scope { get; private set; }

        #endregion Properties

        #region Constructor
        public PerformSynchronize(ClientSyncDS.TaskSyncListRow task)
        {
            TaskSync = task;
            UrlProvider = ClientSyncContext.UrlProvider;
            clientId = (Guid)CommonVariable.Get(ClientConst.ClientID);
            SyncStartTime = SyncEndTime = DateTime.Now;
        }
        public PerformSynchronize(ClientSyncDS.TaskSyncListRow task, ScopeInfoEx scope)
        {
            TaskSync = task;
            UrlProvider = ClientSyncContext.UrlProvider;
            clientId = (Guid)CommonVariable.Get(ClientConst.ClientID);
            SyncStartTime = SyncEndTime = DateTime.Now;
            Scope = scope;
        }
        #endregion

        #region method
        private void EndSelfWCFClient()
        {
            if (ProxyInfo != null && selfCreate1)
            {
                if (_wcfConnection != null)
                    _wcfConnection.Close();
                else
                    if (ProxyInfo.ChannelFactory.State != System.ServiceModel.CommunicationState.Closed)
                        _ProxyInfo.ChannelFactory.Close();

                _ProxyInfo = null;
            }
        }
        /// <summary>
        /// Lấy thống kê về phiên làm việc tại remote
        /// </summary>
        /// <returns></returns>
        KSyncStatistics GetRemoteStatistics()
        {
            int trys = 0;
            do
            {
                try
                {
                    return ProxyInfo.Channel.GetSyncStatistics();
                }
                catch (FaultException<WebSyncFaultException> fe)
                {
                    Log.WarnFormat("Try get SyncStatistics from remote but WebSyncFaultException({0}) :\n{1}:{2}"
                        , trys, fe.Detail.Message, fe.Detail.InnerException);
                    continue;
                }
                catch (FaultException<DataFaultException> fe)
                {
                    if (trys >= 3)
                        throw fe;
                    Log.Warn(string.Format("Try get SyncStatistics from remote but DataFaultException({0}) :\n{1}", trys, fe.Detail.Message));
                    continue;
                }
                catch (Exception ex)
                {

                    throw ex;
                }
                finally
                {
                    trys++;
                }
            }
            while (trys < 3);
            return null;
        }
        /// <summary>
        /// Kiểm tra thông tin cần thiết trước khi đồng bộ
        /// </summary>
        void CheckBeforeSync()
        {
            //Check Assembly exists           
            MonitorEvent.Monitor.Info("Kiểm tra tham số");
            string assPath = Application.StartupPath + "\\" + Scope.ClientAssembly;
            if (!File.Exists(assPath))
            {
                if (!Directory.Exists(Path.GetDirectoryName(assPath)))
                {
                    Directory.CreateDirectory(Path.GetDirectoryName(assPath));
                    int tryCount = 0;
                    //Download Assembly From server
                    do
                    {
                        try
                        {
                            byte[] datas = ProxyInfo.Channel.GetAssembly(Scope.ScopeExId);
                            break;
                        }
                        catch (Exception ex)
                        {
                            if (ProxyInfo.ChannelFactory.State == CommunicationState.Faulted || tryCount > 3)
                                throw ex;
                            tryCount++;
                        }
                    } while (true);
                }
            }
        }

        /// <summary>
        /// Kiểm tra xem Proxy tới WCF hiện tại có hợp lệ không
        /// </summary>
        /// <returns></returns>
        bool CanUseProxy()
        {
            return ProxyInfo.ChannelFactory.State == CommunicationState.Opened;
        }
        void FinallyTask()
        {
            try
            {
                ClientSyncContext.CurrentAgent = null;
                CallRelease();
                CallClearSession();
                EndSelfWCFClient();

            }
            catch (FaultException<WebSyncFaultException> fe)
            {
                _Animation.Warning();
                Log.Fatal("FinallyTask Exception:" + fe.Detail.InnerException);
            }
            catch (Exception ex)
            {
                _Animation.Warning();
                Log.Fatal("FinallyTask Exception", ex);
            }

            CallEndSync();
        }
        void CallEndSync()
        {
            try
            {
                if (KeeperSync != null)
                    KeeperSync.EndSync();
            }
            catch (Exception ex)
            {
                _Animation.Warning();
                Log.Fatal("CallEndSync Exception", ex);
            }
        }
        void CallRelease()
        {
            if (CanUseProxy())
                ProxyInfo.Channel.Release(clientId);
        }
        void CallWaitOne()
        {
            if (CanUseProxy())
            {
                MonitorEvent.Monitor.Info("Lấy quyền đồng bộ");
                int count1 = 0;
                while (true)
                {
                    if (ProxyInfo.Channel.WaitOne(clientId, 2000))
                        break;
                    count1++;
                    MonitorEvent.Monitor.Info("Lấy quyền đồng bộ(" + count1.ToString() + ")");
                    Thread.Sleep(2000);
                }
            }
        }
        void CallClearSession()
        {

        }
        void Authentication()
        {
            byte[] tokenByte = null;
            //string token= SqlHelper.ExecuteScalar(CommonVariable.SqlConnectionString,CommandType.Text, "Select ISNULL(Token,' ') from ClientSync Where ClientId='" + clientId.ToString() + "'").ToString();
            //if (!(token.Trim() == ""))
            //    tokenByte=Convert.FromBase64String(token);
            // else
            //    tokenByte = Guid.Empty.ToByteArray();

            bool isSuccess = ProxyInfo.Channel.Authentication(clientId, tokenByte, CommonVariable.Get("UserName").ToString(), CommonVariable.Get("Password").ToString());
            if (!isSuccess)
            {
                throw new Exception("Đăng nhập thất bại");
            }
            //SqlHelper.
        }
        void LogOperatorStatistics()
        {
            try
            {
                SyncEndTime = DateTime.Now;
                KeeperSyncUtility.SaveSyncLog(SyncStartTime, SyncEndTime, AgentStatistics, LocalKStatistics, RemoteKStatistics, Scope, ResultCode, Mode.ToString(), syncDescription);

            }
            catch (Exception ex)
            {
                if (TaskSync.StatusCode != ClientConst.TaskError)
                {
                    TaskSync.StatusCode = ClientConst.TaskWarning;
                    _Animation.Warning();
                }
                TaskSync.Message += "------------\nLog Operator Exception\n" + ex.ToString();
                TaskSync.Status = "Không thể lưu log";
                Log.Fatal("LogOperatorStatistics Exception", ex);
            }
        }
        void SyncAgent_StateChanged(object sender, SyncOrchestratorStateChangedEventArgs e)
        {
            switch (e.NewState)
            {
                case SyncOrchestratorState.Downloading:
                    MonitorEvent.Monitor.RaiseEvent(SyncEventType.Info, "", "Cập nhật dữ liệu từ máy chủ");
                    break;
                case SyncOrchestratorState.Uploading:
                    MonitorEvent.Monitor.RaiseEvent(SyncEventType.Info, "", "Đẩy dữ liệu lên máy chủ");
                    break;
                case SyncOrchestratorState.UploadingAndDownloading:
                    MonitorEvent.Monitor.RaiseEvent(SyncEventType.Info, "", "Đang cập nhật dữ liệu");
                    break;
            }
        }
        #endregion method
        #region Operator
        public void Synchronize()
        {
            AnimationGrid animation = null;
            try
            {


                Step = PerformSyncStep.Prepare;
                ClientSyncContext.CurrentTask = TaskSync;

                if (_Animation == null)
                    _Animation = new AnimationGrid();
                animation = _Animation;
                ClientSyncContext.OnSyncProcess(EventType.BeginTask);
                animation.Start();
                if (Scope == null)
                {

                    Scope = new ScopeInfoEx();
                    Scope.ScopeName = TaskSync.ScopeName;

                }
                SyncAgent = new SyncOrchestrator();

                ClientSyncContext.CurrentAgent = SyncAgent;
                ProxyInfo.Channel.KeepAlive();
                Authentication();
                if (Scope.ScopeExId == Guid.Empty)
                {
                    MonitorEvent.Monitor.Info("Lấy thông tin phạm vi");
                    ScopeInfoEx vScope = ProxyInfo.Channel.GetScopeInfoByName(TaskSync.ScopeName, clientId);
                    if (vScope == null)
                        throw new Exception("Không thể lấy được thông tin phạm vi " + TaskSync.ScopeName);
                    else
                        Scope = vScope;
                }
                if (Scope.Enable == false)
                {

                    syncDescription = "Không thể thực hiện đồng bộ phạm vi " + Scope.ScopeName + Environment.NewLine + "Phạm vi này đã được vô hiệu hóa bởi người quản trị ";

                    ResultCode = "1";
                    SetStatusAndMessage(ClientConst.DesTaskSkip, syncDescription, ClientConst.TaskWarning);
                    animation.Skip();
                    return;
                }
                CallWaitOne();

                //Khởi tạo đối tượng KeeperSync
                MonitorEvent.Monitor.Info("Khởi tạo đối tượng");
                string classPath = "";
                KeeperSync = KeeperSyncUtility.CreateKeeperSync(Scope, Application.StartupPath, 0, out classPath);
                if (KeeperSync == null)
                    throw new Exception("Khởi tạo đối tượng IKeeperSync không thành công(NULL)(ScopeName=" + Scope.ScopeName +
                        " ClientAssembly=" + Scope.ClientAssembly + " ClientClass=" + Scope.ClientClass + ")");
                KeeperSync.ClientId =(Guid) CommonVariable.Get(ClientConst.ClientID);
                TaskSync.ClientClass = classPath;
                KeeperSync.ProxyInfo = ProxyInfo;

                _RemoteProvider = new KSyncProviderProxy(ProxyInfo);
                _RemoteProvider.Initialize(Scope);

                NextAction action = KeeperSync.Initialize(Scope, SyncProviderPosition.Local, out message);
                if (action != NextAction.Continue)
                    throw new Exception(string.Format("IKeeperSync {0} gọi phương thức [Initialize] mã trả về [Skip] với thông điệp \"{1}\"", Scope.ServerClass, message));

                action = KeeperSync.PrepareSync(out message);
                if (action == NextAction.Skip)
                {
                    throw new Exception(
                        string.Format("IKeeperSync {0} gọi phương thức [PrepareSync] mã trả về [Skip] với thông điệp \"{1}\"",
                                        Scope.ServerClass, message));
                }

                action = KeeperSync.BeginSync(out message);
                if (action == NextAction.Skip)
                {
                    throw new Exception(
                        string.Format("IKeeperSync {0} gọi phương thức [BeginSync] mã trả về [Skip] với thông điệp \"{1}\" \"{1}\"",
                                        Scope.ServerClass, message));
                }

                if (KeeperSync.Provider == null)
                    throw new Exception(Scope.ServerClass + " Provider is null");

                SyncAgent.LocalProvider = KeeperSync.Provider;
                SyncAgent.RemoteProvider = _RemoteProvider;
                SyncAgent.Direction = Scope.SyncDirection;
                SyncAgent.StateChanged += new EventHandler<SyncOrchestratorStateChangedEventArgs>(SyncAgent_StateChanged);
                MonitorEvent.Monitor.RaiseEvent(SyncEventType.Info, "", "Thực hiện đồng bộ");
                Step = PerformSyncStep.Running;
                this.AgentStatistics = SyncAgent.Synchronize();
                ProxyInfo.Channel.UpdateSyncTimes(clientId, Scope);
                syncDescription = "OK";
                Step = PerformSyncStep.Logging;
                LocalKStatistics = KeeperSync.SyncStatistics;
                SessionId = LocalKStatistics.SessionId;
                RemoteKStatistics = GetRemoteStatistics();
                SetStatusAndMessage(ClientConst.DesTaskSuccess, syncDescription, ClientConst.TaskSuccess);
                ResultCode = "0";
                animation.Success();

            }
            catch (FaultException<WebSyncFaultException> fe)
            {
                Log.Fatal(string.Format("Synchronize scope {0} but WebSyncFaultException:", Scope.ScopeName), fe);
                ResultCode = "-1";
                syncDescription = fe.Detail.InnerException;
                Message = syncDescription;
                if (Step != PerformSyncStep.Logging)
                {
                    SetStatusAndMessage(ClientConst.DesTaskError, syncDescription, ClientConst.TaskError);
                    animation.Error();
                }
                else
                {
                    SetStatusAndMessage(ClientConst.DesTaskWarning, syncDescription, ClientConst.TaskWarning);
                    ResultCode = "1";
                    animation.Warning();
                }

            }
            catch (CommunicationException ce)
            {
                //Connection to WCF Fail
                Log.Fatal(string.Format("Synchronize scope {0} but CommunicationException:", Scope.ScopeName), ce);
                ResultCode = "-1";
                syncDescription = ce.ToString();
                if (Step != PerformSyncStep.Logging)
                {
                    SetStatusAndMessage(ClientConst.DesTaskError, syncDescription, ClientConst.TaskError);
                    animation.Error();
                }
                else
                {
                    SetStatusAndMessage(ClientConst.DesTaskWarning, syncDescription, ClientConst.TaskWarning);
                    ResultCode = "1";
                    animation.Warning();
                }

            }
            catch (Exception ex)
            {
                Log.Fatal(string.Format("Synchronize scope {0} but Exception:", Scope.ScopeName), ex);
                ResultCode = "-1";
                syncDescription = ex.ToString();

                if (Step != PerformSyncStep.Logging)
                {

                    SetStatusAndMessage(ClientConst.DesTaskError, syncDescription, ClientConst.TaskError);
                    animation.Error();
                }
                else
                {
                    SetStatusAndMessage(ClientConst.DesTaskWarning, syncDescription, ClientConst.TaskWarning);
                    ResultCode = "1";
                    animation.Warning();
                }

            }
            finally
            {
                LogOperatorStatistics();
                FinallyTask();
                ClientSyncContext.OnSyncProcess(EventType.FinishTask);
                animation.Stop();
                animation.Dispose();
            }
        }

        void SetStatusAndMessage(string stt, string msg, string sttCode)
        {
            if (ClientSyncContext.CurrentTask != null)
            {
                ClientSyncContext.CurrentTask.Status = stt;
                ClientSyncContext.CurrentTask.StatusCode = sttCode;
                ClientSyncContext.CurrentTask.Message = msg;
            }

        }

        #endregion Operator

        void SetTaskSessiongId(Guid session)
        {
        }

    }
    public enum PerformSyncStep
    {
        Prepare,
        Running,
        Logging,
        Done
    }


}
